package genetique;

import java.util.ArrayList;
import java.util.Date;
import java.util.Random;

import robot.IRobot;

/**
 * Class représantant l'évolution génétique
 * @author max
 *
 */
public class Evolution {
	/**
	 * probabilité de grande mutation
	 */
	private final static double PROBA_GR_MUTATION = 0.1;

	/**
	 * probabilité de petite mutation
	 */
	private final static double PROBA_PT_MUTATION = 0.9;

	/**
	 * Pas de grande mutation
	 */
	private final static double PAS_GR_MUTATION = 10 ;

	/**
	 * Pas de petite mutation
	 */
	private final static double PAS_PT_MUTATION = 0.1 ;

	/**
	 * Seuil au dessus duquel le robot gardé pour la génération suivante
	 */
	private final static double seuil = 0.9 ;

	/**
	 * mutation d'un ADN
	 * @param genes
	 */
	public static void mutation( IRobot robot ) {

		double[][] genes = robot.getMatricePoids() ;

		double hasard = 0;
		boolean negatif = false;
		double signe = 0;

		// Initialisation du générateur de nombre aléatoire
		Random r = new Random( System.nanoTime() );

		//Pour tous les genes
		for( int gene=0; gene<genes.length; ++gene ) {
			//Pour tous les nucleotides du gene
			for( int nucleo=0; nucleo<genes[gene].length; ++nucleo ) {

				hasard = r.nextDouble() ;
				negatif = r.nextBoolean() ;
				//Changement de signe avec une proba de 0.5 (en moyenne une fois sur deux)
				if( negatif  ) signe = -1 ;
				else signe = 1 ;

				//Mutation du nucleotide
				if( hasard < PROBA_GR_MUTATION ) {
					genes[gene][nucleo] = genes[gene][nucleo] + signe * PAS_GR_MUTATION ;
				}
				else if( hasard < PROBA_PT_MUTATION ) {
					genes[gene][nucleo] = genes[gene][nucleo] + signe * PAS_PT_MUTATION ;
				}

			}

		}

		robot.setMatricePoids( genes );

	}

	/**
	 * Croisement 
	 */
	public static void croisement( IRobot robot, ArrayList<IRobot> listeRobots ) {
		int robot2 ;
		int neuron ;
		double [] poids = new double [robot.getMatricePoids().length];

		Random r = new Random( System.nanoTime() );

		robot2 = r.nextInt( listeRobots.size() ) ;

		robot2 = r.nextInt( listeRobots.size() ) ;

		neuron = r.nextInt( robot.getMatricePoids().length ) ;

		for( int i=0; i<robot.getMatricePoids()[neuron].length; ++i ) {
			poids[i] = robot.getMatricePoids()[neuron][i] ;
		}
		for( int i=0; i<robot.getMatricePoids()[neuron].length; ++i ) {
			robot.getMatricePoids()[neuron][i]  = listeRobots.get(robot2).getMatricePoids()[neuron][i] ;
		}
		for( int i=0; i<robot.getMatricePoids()[neuron].length; ++i ) {
			listeRobots.get(robot2).getMatricePoids()[neuron][i] = poids[i] ;
		}
	}


	/**
	 * Reproduit la population d'individu vers la generation suivante.
	 */
	public static void reproduction( ArrayList<IRobot> listeRobots ) {
		double hasard ;
		double score = 0;
		int index_robot = 0;
		int next_robot = 0 ;

		// Date pour initialisation du random
		Date d = new Date();
		// Initialisation du générateur de nombre aléatoire
		Random r = new Random( System.nanoTime() );

		while( next_robot < listeRobots.size() ) {
			//On selectionne un agent au hasard en fct de son score
			hasard = r.nextDouble() ;
			score = listeRobots.get(index_robot).getScore() ;

			//on parcours le tableau
			//if random - score < seuil
			if( hasard + score > seuil ) {//Reproduction avec prise en compte du seuil
				//if( hasard + 0.5 > seuil ) {//Reproduction sans prise en compte du seuil
				//On l'ajoute à la génération suivante
				listeRobots.get(index_robot).setMatricePoids( listeRobots.get(next_robot).getMatricePoids() ) ;
				++next_robot ;
				if( 0.3 > r.nextDouble() ) {
					//Il mute
					Evolution.mutation( listeRobots.get(index_robot) ) ;
				}
				else {
					//Il est croisé avec un autre individu
					croisement(listeRobots.get(index_robot), listeRobots) ;
				}
			}

			//Passage au robot suivant
			index_robot = (++index_robot) % listeRobots.size() ;

		}

	}
}
